package lan;

//import cti.item_system.player.Attender;
import java.util.*;
import java.awt.*;
import java.net.InetAddress;
import java.net.NetworkInterface;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import javax.swing.tree.*;

/**
 * This is responsible for GUI part of LanBrowser application
 * @author J. Seetharaman
 */
class LanFrame extends JFrame implements TreeSelectionListener
{
    /**
     * progress: the first gui component displayed at the center.  
     * <br>Progresses till the scaning of alive LAN Components (currently 
     * 192.168.0.1 to 192.168.0.255) is completed
     */
    private JProgressBar progress; 
    /**
     * scrSize: current screen size
     */
    private Rectangle scrSize;
    /**
     * panProgress: JPanel contains Progress info. of Lan Scanning (lblStatus)
     * and corresponding progress bar (progress)
     */
    private JPanel panProgress;
    /**
     * lblProgress: label displays the Progress info. of Lan Scanning
     */
    private JLabel lblProgress;
    /**
     * pinger: object of inner class (Pinger), uses threads to ping the LAN 
     * components
     */
    protected Pinger pinger;
    /**
     * panStatus: Panel for status bar
     */
    private JPanel panStatus;
    private JLabel lblStatus;
    /**
     * childProgress: Indeterminate progress bar displayed in status bar, 
     * stays till the scaning and categarisation of nodes are over, 
     */
    private JProgressBar childProgress;
    /**
     * snmpDevices: vector stores the snmp components found in LAN
     */
    private Vector snmpDevices;
    /**
     * nonSnmpDevices: vector stores the non-snmp components found in LAN
     */
    private Vector nonSnmpDevices;
    /**
     * neighbours: vector stores all the components found in LAN
     */
    private Vector neighbours=new Vector();
    private JSplitPane splitPane;
    /**
     * tree: displayed as left side component of splitPane, lists the found LAN
     * components under SNMP and Non-SNMP groups
     */
    private JTree tree;
    /**
     * scrPaneTree:Scroll pane consists the tree
     */
    private JScrollPane scrPaneTree;
    private DefaultTreeModel treeModel;
    private DefaultMutableTreeNode rootNode;
    private DefaultMutableTreeNode snmpNode;
    private DefaultMutableTreeNode nonSnmpNode;
    /**
     * snmpFriend: object of SnmpFriend (user-defined) class, supports the SNMP
     * requirements
     */
//    private SnmpFriend snmpFriend;
    /**
     * hashNodeResult: Hashtable consists the selected node details as:
     * key  : childnode (in mib module) name (String)
     * value: Vector of all children's (in mib module) Mib-2 values as 
     * individual Properties objects
     */
    private Hashtable hashNodeResult;
    /**
     * propSysDetails: Properties, holds the Mib-2 values of selected node
     */
    private Properties propSysDetails;    
    /**
     * border: border for right side panel of split pane
     */
    private TitledBorder border;
    /**
     * panDetails: Panel, the side panel of split pane
     */
    private JPanel panDetails;
    /**
     * tabPaneDetails: a tabbed pane displayed in panDetails, <br>
     * displays the Mib-2 and corresponding children properties
     */
    private JTabbedPane tabPaneDetails;
    private InetAddress[] localIPs;
    private String[] localAddrs;
    private String osName;
   /**
    * the constructor 
    * @throws java.lang.Exception
    */
    public LanFrame() throws Exception
    {        
        super("LAN Explorer");
        /**
         * switch off the bold metal model of Swing GUI
         */
        UIManager.put("swing.boldMetal", Boolean.FALSE);
        setLayout(null);
        scrSize=getGraphicsConfiguration().getBounds();
        setBounds(scrSize);
        setResizable(false);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);      
        
        lblStatus=new JLabel("Status:");
        lblStatus.setBounds(5,5,700,20);
        
        childProgress=new JProgressBar();
        childProgress.setBounds((int)(scrSize.getWidth()-150),5,130,20);                
        /**
         * if u dont know the time limit for progress
         */
        childProgress.setIndeterminate(true);
        
        panStatus=new JPanel(null);
        panStatus.setBounds(0,(int)scrSize.getHeight()-50,(int)(scrSize.getWidth()-5),30);
        panStatus.add(lblStatus);
        panStatus.add(childProgress);
        panStatus.setBorder(BorderFactory.createEtchedBorder());
        add(panStatus);
        
        displayScanDlg();                
        setVisible(true); 
        /**
         * frame is disabled until the scanning and categorisation is over
         */
        setEnabled(false);
        setInetAddress();
        findNeighbours();
        
       /**
         * removes the panProgress when scaning is done
         */
        remove(panProgress);        
        repaint();
        /**
         * when scanning is over, create split pane with initial tree (with snmp
         * and non-snmp nodes, ready to add children under...)
         */
        createSplitPane();        
        /**
         * loads the mib files, map devices with mib modules,...
         */
//        snmpFriend=new SnmpFriend();
        lblStatus.setText("Status: "+neighbours.size()+" Nodes Found, Categorizing them into SNMP and Non-SNMP Groups...");
        categorizeNodes();
        add(splitPane);
        /**
         * when categorization is over, childprogress can be hide
         */
        childProgress.setVisible(false);
        lblStatus.setText("Status: Explore Individual Tree Node for details...");        
        /**
         * when categorization is over, frame is enabled
         */
        setEnabled(true);
    }  
    
    private void setInetAddress()throws Exception
    {
        //if !windows
        osName=System.getProperty("os.name");
        System.out.println("os: "+osName);
        if(osName.indexOf("Windows")!=-1)
        {
            localIPs=InetAddress.getAllByName(InetAddress.getLocalHost().getHostName());
            localAddrs=new String[localIPs.length];
            System.out.println("IPs found for localhost: "+localAddrs.length);
            boolean staticIP=false;
            for(String s:localAddrs)
            {
                System.out.println(s);
                if(s!=null)
                {
                    staticIP=true;
                    break;
                }
            }
            System.out.println("is it static: "+staticIP);
            if(!staticIP)
            {
                localIPs=new InetAddress[1];
                localAddrs=new String[localIPs.length];
                localIPs[0]=InetAddress.getLocalHost();
            }       
        }
        else    //linux
        {
            System.out.println("in linux");
            Enumeration<NetworkInterface> nis=NetworkInterface.getNetworkInterfaces();
            while(nis.hasMoreElements())
            {
                NetworkInterface ni=nis.nextElement();
                if(!ni.isLoopback())
                {
                    String nam=ni.getDisplayName();
                    System.out.println(nam);
                    if((nam.indexOf("eth")!=-1)||(nam.indexOf("wlan")!=-1))
                    {
                        Enumeration<InetAddress> inets=ni.getInetAddresses();
                        int tmpCount=0;                                               
                        while(inets.hasMoreElements())
                        {
                            String tmp=inets.nextElement().toString();
                            if(tmp.indexOf("192.168.")!=-1)
                            tmpCount++;
                        }
                        System.out.println(tmpCount);
                        localIPs=new InetAddress[tmpCount];
                        int i=0;
                        inets=ni.getInetAddresses();
                        while(inets.hasMoreElements())
                        {
                            InetAddress tmp=inets.nextElement();
                            if(tmp.toString().indexOf("192.168.")!=-1)
                                localIPs[i++]=tmp;
                        }
                        localAddrs=new String[localIPs.length];
                        System.out.println("IPs found for localhost: "+localIPs.length);
                        for(InetAddress inet:localIPs)
                            System.out.println(inet);
                    }
                    if(localIPs.length!=0)
                        break;
                }
            }
        }   
    
    }
    private void findNeighbours()throws Exception
    {
        for(int i=0;i<localIPs.length;i++)
        {
            localAddrs[i]=localIPs[i].toString().substring(localIPs[i].toString().indexOf("/")+1,localIPs[i].toString().lastIndexOf(".")+1);
            System.out.println("Finding Neighbours in "+localAddrs[i]);            
            Pinger pinger=new Pinger(localAddrs[i]);  
            Vector tmp=pinger.getNeighbours();
            for(Object o:tmp)
                neighbours.add(o.toString());
        }
        System.out.println("Neighbours Found: "+neighbours.size());        
    }
    /**
     * creates split pane with initial tree (with snmp and non-snmp nodes, 
     * ready to add children under...)
     */
    private void createSplitPane()
    {
        snmpNode=new DefaultMutableTreeNode("SNMP");        
        nonSnmpNode=new DefaultMutableTreeNode("Non-SNMP");
        rootNode=new DefaultMutableTreeNode("MyLAN");        
        rootNode.add(snmpNode);
        rootNode.add(nonSnmpNode);
        treeModel=new DefaultTreeModel(rootNode);        
        tree=new JTree(treeModel); 
        tree.addTreeSelectionListener(this);
        scrPaneTree=new JScrollPane(tree);
        /**
         * else, the user can move the spliter to the left end 
         * (makes the tree invisible)
         */
        scrPaneTree.setMinimumSize(new Dimension(200,(int)scrSize.getHeight()));
        
        panDetails=new JPanel(null);
        border=BorderFactory.createTitledBorder("Node Details");
        /**
         * border title is made bold
         */
        border.setTitleFont(new Font(border.getTitleFont().getFontName(),Font.BOLD,border.getTitleFont().getSize()));        
        border.setTitleJustification(TitledBorder.CENTER);
        panDetails.setBorder(border);
        /**
         * else, the user can move the spliter to the right end 
         * (makes the right side panel invisible)
         */
        panDetails.setMinimumSize(new Dimension(600,(int)scrSize.getHeight()));   
        
        splitPane=new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,true,scrPaneTree,panDetails);        
        splitPane.setBounds(5,5,(int)(scrSize.getWidth()-10),(int)(scrSize.getHeight()-60));
        splitPane.setDividerLocation(0.25);
        /**
         * minimize/restores the left/right components in single click
         */
        splitPane.setOneTouchExpandable(true);         
    }
    /**
     * displays the initial progress pane at the center
     */
    public void displayScanDlg()
    {                
        lblProgress=new JLabel("Wait... Gathering LAN Info...");
        lblProgress.setBounds(10,10,170,20);
        /**
         * progress for 255 nodes
         */
        progress=new JProgressBar(0,255);
        /**
         * makes the progress string visible
         */
        progress.setStringPainted(true);
        progress.setBounds(10,40,170,20);
        panProgress=new JPanel(null);        
        panProgress.setBounds((int)scrSize.getWidth()/3,(int)scrSize.getWidth()/3,200,100);     
        panProgress.add(lblProgress);
        panProgress.add(progress);       
        add(panProgress);
    }  
    /**
     * categorizes found Lan components (available in 'neighbours') into SNMP
     * and Non-Snmp, and adds them under appropriate parent tree nodes 
     * @throws java.lang.Exception
     */
    private void categorizeNodes() throws Exception
    {        
        snmpDevices=new Vector();
        nonSnmpDevices=new Vector();
        for(Object o:neighbours)
        {                
//            lblStatus.setText("Status: "+" Total Nodes: "+neighbours.size()+" Categorizing in Process... SNMP Nodes: "+snmpDevices.size()+" ;Non-SNMP Nodes: "+nonSnmpDevices.size()+"            Checking: "+o.toString());
//            /**
//             * isSnmpEnabled: used-defined, returns true if the arg responses
//             * for snmp query else return false
//             */
//            if(snmpFriend.isSnmpEnabled(o.toString()))
//            {
//                snmpDevices.add(o);
//                snmpNode.add(new DefaultMutableTreeNode(o.toString()));
//            }
//            else
//            {
                nonSnmpDevices.add(o);
                nonSnmpNode.add(new DefaultMutableTreeNode(o.toString()));                
//            }
        }   
        /**
         * UserObject: the visible component in a tree node i.e. the caption
         */
//        snmpNode.setUserObject("SNMP("+snmpDevices.size()+")");
        nonSnmpNode.setUserObject("Non-SNMP("+nonSnmpDevices.size()+")");                    
        tree.updateUI();                
    } 
    /**
     * fired when a node is selected
     * @param e, the TreeSelectionEvent, gives the info. about the selected node
     */
    public void valueChanged(TreeSelectionEvent e) 
    {           
        /**
         * existing right side components are removed on making a new selection 
         */
        int tmpGeneralTabIndex=0;
        panDetails.removeAll();
        tabPaneDetails=new JTabbedPane();
        tabPaneDetails.setBounds(20,20,panDetails.getWidth()-50,600); 
        /** 
         * the selected node
         */
        DefaultMutableTreeNode nodeSelected = 
                (DefaultMutableTreeNode)tree.getLastSelectedPathComponent();
        if(nodeSelected.isLeaf())
        {            
            if(nodeSelected.getParent().toString().trim().startsWith("SNMP"))
            {               
//                /**
//                 * retrieves the selected snmp node details
//                 */
//                hashNodeResult=snmpFriend.getNodeDetails(nodeSelected.toString());
//                Enumeration enumKeys=hashNodeResult.keys();
//                while(enumKeys.hasMoreElements())
//                {
//                    String key=enumKeys.nextElement().toString();
//                    /**
//                     * in hashNodeResult, the key 'Mib-2'is assigned with value
//                     * of 'Properties' type
//                     */
//                    if(key.equals("Mib-2"))
//                    {
//                        propSysDetails=(Properties)hashNodeResult.get(key);
//                        tabPaneDetails.addTab("General",getTabContent(propSysDetails));
//                        tmpGeneralTabIndex=tabPaneDetails.getTabCount()-1;
//                    }
//                    /**
//                     * in hashNodeResult, the keys other than 'Mib-2'are
//                     * assigned with values of 'Vector' type
//                     * <br> each vector will have multiple 'Properties' entry
//                     * for Mib-2 values of every leaf mib node
//                     * <br>So, except 'general' tab, the other tabs may have
//                     * nested tabbed panes for respective leaf nodes of every
//                     * mib children
//                     */
//                    else
//                    {
//                        /**
//                         * tmpVecChildren: holds the first level children
//                         */
//                        Vector tmpVecChildren=(Vector)hashNodeResult.get(key);
//                        JTabbedPane tmpTabPaneChildren=new JTabbedPane();
//                        Enumeration tmpEnumChildren=tmpVecChildren.elements();
//                        while(tmpEnumChildren.hasMoreElements())
//                        {
//                            /**
//                             * every tmpProp consists mib-2 values for
//                             * 2nd level children
//                             */
//                            Properties tmpProp=(Properties)tmpEnumChildren.nextElement();
//                            /**
//                             * adding leaf node tabs to nested tab
//                             */
//                            tmpTabPaneChildren.addTab(tmpProp.getProperty("Leaf Node Name"),getTabContent(tmpProp));
//                        }
//                        /**
//                         * adding the main child tab
//                         */
//                        tabPaneDetails.addTab(key,tmpTabPaneChildren);
//                    }
//                }
//                panDetails.add(tabPaneDetails);
//                /**
//                 * border displays the info. about the selected tree node
//                 */
//                tabPaneDetails.setSelectedIndex(tmpGeneralTabIndex);
//                border.setTitle("Node Details: "+nodeSelected.toString());
            }                
            /**
             * what to display!!
             */
            if(nodeSelected.getParent().toString().trim().startsWith("non-snmp"))
            {
                System.out.println("non-snmp node selected");                
            }                         
            splitPane.updateUI();        
        }
    }
    /**
     * prepares content panel for the leaf node with the given properties 
     * @param argPropDetails, the mib-2 information about the leaf mib node
     * @return the prepared jscrollpane with the leaf node details
     */
    private JScrollPane getTabContent(Properties argPropDetails) 
    {        
        /**
         * added as viewport component for scrollpane
         */
        JPanel tmpPanDetails;
        /**
         * dynamic labels with properties' key entries
         */
        JLabel[] tmpLblKeys;
        /**
         * dynamic textaread with properties' value entries
         */
        JTextArea[] tmpTxtVals;
        /**
         * dynamic scrollpanes holds the tmpTxtVals
         */
        JScrollPane[] tmpScrPanVals;
        Enumeration enumKeys;
        String key;
        /**
         * useful for setting bounds of dynamic components of panel
         */
        int tmpCountKeys=0;
        /**
         * the prepared scrollpane to be returned
         */
        JScrollPane tmpScrPanDetails; 
        JPanel tmpPanSyntax;
        TitledBorder tmpSyntaxBorder;
        
        tmpPanDetails=new JPanel(null);         
        
        tmpLblKeys=new JLabel[argPropDetails.size()];
        tmpTxtVals=new JTextArea[argPropDetails.size()];
        tmpScrPanVals=new JScrollPane[argPropDetails.size()];
                
        enumKeys=argPropDetails.keys();        
        
        while(enumKeys.hasMoreElements())
        {
            key=enumKeys.nextElement().toString();
            if(key.equals("Syntax"))
            {                  
                tmpPanSyntax=new JPanel(null);
                tmpPanSyntax.setBounds(400,5,250,400);                
                String tmp1=argPropDetails.getProperty("Syntax");                
                String tmp2=tmp1.substring(1,tmp1.indexOf("}"));                
                String[] tmpSyntaxEntries=tmp2.split(",");
                JLabel[] tmpLblSyntaxKeys=new JLabel[tmpSyntaxEntries.length];
                JComboBox cmbLbl;
                JTextField[] txtSyntaxVals;
                if(tmp2.indexOf("Valid Options")==-1)
                    txtSyntaxVals=new JTextField[tmpSyntaxEntries.length];
                else
                    txtSyntaxVals=new JTextField[tmpSyntaxEntries.length-1];
                int txtCount=0;
                for(int i=0;i<tmpSyntaxEntries.length;i++)
                {
                    String[] keyVal=tmpSyntaxEntries[i].split("=");                    
                    tmpLblSyntaxKeys[i]=new JLabel(keyVal[0]+":  ",SwingConstants.RIGHT);
                    if(i==0)
                        tmpLblSyntaxKeys[i].setBounds(10,50,100,20);
                    else                        
                        tmpLblSyntaxKeys[i].setBounds(10,tmpLblSyntaxKeys[i-1].getY()+50,100,20);
                    tmpPanSyntax.add(tmpLblSyntaxKeys[i]);
                    
                    if(keyVal[0].trim().equals("Valid Options"))
                    {
                        cmbLbl=new JComboBox(keyVal[1].split(";"));
                        if(i==0)
                            cmbLbl.setBounds(120,50,100,20);
                        else
                            cmbLbl.setBounds(120,tmpLblSyntaxKeys[i-1].getY()+50,100,20);
                        tmpPanSyntax.add(cmbLbl);                    
                    }
                    else
                    {
                        txtSyntaxVals[txtCount]=new JTextField();
                        txtSyntaxVals[txtCount].setText(keyVal[1]);                    
                        txtSyntaxVals[txtCount].setEnabled(false);                    
                        txtSyntaxVals[txtCount].setDisabledTextColor(Color.BLACK);                    
                        if(i==0)
                            txtSyntaxVals[txtCount].setBounds(120,50,100,20);
                        else
                            txtSyntaxVals[txtCount].setBounds(120,tmpLblSyntaxKeys[i-1].getY()+50,100,20);
                        tmpPanSyntax.add(txtSyntaxVals[txtCount]);                    
                        txtCount++;    
                    }                    
                }
                tmpSyntaxBorder=BorderFactory.createTitledBorder("Syntax:");
                tmpPanSyntax.setBorder(tmpSyntaxBorder);
                tmpPanDetails.add(tmpPanSyntax);
            }
            else
            {
                tmpLblKeys[tmpCountKeys]=new JLabel(key+":  ",SwingConstants.RIGHT);
                if(tmpCountKeys==0)
                    tmpLblKeys[tmpCountKeys].setBounds(5,5,150,20);            
                else
                    /**
                     * dont use previous label's 'Y' value, scrPan are bigger than
                     * these labels
                     */
                tmpLblKeys[tmpCountKeys].setBounds(5,tmpScrPanVals[tmpCountKeys-1].getY()+50,150,20);
                tmpPanDetails.add(tmpLblKeys[tmpCountKeys]);
                tmpTxtVals[tmpCountKeys]=new JTextArea();
                tmpTxtVals[tmpCountKeys].setText(argPropDetails.getProperty(key,""));
                /**
                 * by default this is false!!
                 */
                tmpTxtVals[tmpCountKeys].setLineWrap(true);
                tmpTxtVals[tmpCountKeys].setWrapStyleWord(true);
                tmpTxtVals[tmpCountKeys].setEnabled(false);
                /**
                 * dont use setForeground(Color),which works only when the component
                 * is enabled
                 */
                tmpTxtVals[tmpCountKeys].setDisabledTextColor(Color.BLACK);
                tmpScrPanVals[tmpCountKeys]=new JScrollPane(tmpTxtVals[tmpCountKeys]);
                if(tmpCountKeys==0)
                    tmpScrPanVals[tmpCountKeys].setBounds(160,5,200,50);
                else
                    tmpScrPanVals[tmpCountKeys].setBounds(160,tmpScrPanVals[tmpCountKeys-1].getY()+50,200,50);
                tmpPanDetails.add(tmpScrPanVals[tmpCountKeys]);                        
                tmpCountKeys++;
            }
        }        
                
        tmpScrPanDetails = new JScrollPane(tmpPanDetails);        
        /**
         * yes required, otherwise not all the panel components are displayed 
         * and vertical scrollbar is also not appearing
         */
        tmpPanDetails.setPreferredSize(new Dimension(370,tmpScrPanVals[tmpCountKeys-1].getY()+60));        
        return tmpScrPanDetails;
    }
    /*
      testing purpose, it works
     
    public static void main(String[] args) throws Exception
    {
        LanFrame frm=new LanFrame();        
    }
    */
    /**
     * inner (non-static nested) class, uses threads to ping the LAN 
     * components, currently between 192.168.0.1 to 255
     */
    class Pinger 
    {           
        private PingerThread[] pingers=new PingerThread[5];
        private Vector neighbours;
        private String ipClass;
        Pinger(String ipClass) throws Exception
        {
//            Attender.doThis(this, "Register");
            this.ipClass=ipClass;
            neighbours=new Vector(5,2);
            findNeighbours();
            /**
             * let all the threads to finish
             */
            waitForPingers();                      
        }
        /**
         * for testing
         * @param args
         */
         /*public static void main(String[] args)
        {
        try
        {
        Pinger pinger=new Pinger("192.168.0.");         
        } 
        catch (Exception ex)
        {
        ex.printStackTrace();
        }        
        }*/
         /**
          * called by Pinger constructor<br>
          * constructs and starts the pinger threads
          * @throws java.lang.Exception
          */
        private void findNeighbours()throws Exception
        {           
            pingers[0]=new PingerThread(0,50);
            pingers[1]=new PingerThread(50,100);
            pingers[2]=new PingerThread(100,150);
            pingers[3]=new PingerThread(150,200);
            pingers[4]=new PingerThread(200,256); 
            for(Thread t:pingers)
                t.start();
        }
        /**
         * called by DetectEncoder class<br>
         * @return vector of found neighbour components in the given LAN IP Class
         * @throws java.lang.Exception
         */
        public Vector getNeighbours()throws Exception
        {
            return neighbours;
        }
        /**
         * makes the Pinger constructor to wait for the completion of detection process
         * @throws java.lang.Exception
         */
        private void waitForPingers()throws Exception
        {
            for(Thread t:pingers)
                    t.join();
        }
        /**
         * another inner-inner !! class<br>
         * Thread class, which pings the local IPs 
         */
        class PingerThread extends Thread
        {
            private int lower,higher;
            /**
             * constructs the PingerThreads with FROM and TO IP Ranges 
             * @param argLow, the lower ip to start pinging
             * @param argHigh, the last ip to stop pinging
             * @throws java.lang.Exception
             */
            private PingerThread(int argLow, int argHigh) throws Exception
            {
                lower=argLow;
                higher=argHigh;
//                Attender.doThis(this,"register");
            }        
            @Override public void run()
            {
                try
                {
                    for(int i=lower;i<higher;i++)
                    {       
                        System.out.println("Pinging..."+ipClass+i);                        
                        lblStatus.setText("Status: Scanning "+ipClass+i);
                        Process pr=null;
                        if(osName.indexOf("Windows")!=-1)
                        {
                            pr=Runtime.getRuntime().exec("ping -w 50 -n 1 "+ipClass+i);            
                        }
                        else //linux
                        {
                            pr=Runtime.getRuntime().exec("ping "+ipClass+i+" -c 1 -W 1");
                        }
                        int res=pr.waitFor();                 
                        progress.setValue(progress.getValue()+1);
                        System.out.println("res: "+res);                        
//                        Attender.doThis(this,"increase by 1");
                        if(res==0)                            
                            neighbours.add(ipClass+i);                        
                    }       
                } 
                catch (Exception ex)
                {
                    ex.printStackTrace();
                }           
            }
        }
    }    
}